package core.communication;

import core.util.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;


public class ServerConnectorService {

    String url = "http://galaxylegend.ru/gvngconnector.php";

    private static final int RESPONSE_CODE_OK = 200;

    public Document state(String game, String race, String password) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Getting state of application...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);

        response = request(url + "?query=state", params);

        Logger.info("ServerConnectorService", "Getting state of application... COMPLETE");

        return response;
    }

    public Document remove(String game, String race, String password) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Removing application...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);

        response = request(url + "?query=remove", params);

        Logger.info("ServerConnectorService", "Removing application... COMPLETE");

        return response;
    }

    public Document join(String game, String race, String password, String email) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Join to party...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);
        params.put("EMAIL", email);
        params.put("ENCODING", "windows1251");
        params.put("ACCPASS", password);

        response = request(url + "?query=join", params);

        Logger.info("ServerConnectorService", "Join to party... COMPLETE");

        return response;
    }


    public Document loadGameList() throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Loading list of games from server...");

        Map<String, String> params = new HashMap<String, String>();
        response = request(url + "?get=games", params);

        Logger.info("ServerConnectorService", "Loading list of games from server... COMPLETE");

        return response;
    }

    public Document loadMessages(String game, String race, String password, String fileIds) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Loading message bodies from server...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);
        params.put("ID", fileIds);

        response = request(url + "?get=message", params);

        Logger.info("ServerConnectorService", "Loading message bodies from server... COMPLETE");

        return response;
    }

    public Document loadReport(String game, String race, String password, int turn) throws IOException, SAXException, ParserConfigurationException {

        Document response = null;
        long startTime = System.currentTimeMillis();

        Logger.info("ServerConnectorService", "Loading report from server...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);
        params.put("TURN", Integer.toString(turn));

        Logger.debug("ServerConnectorService", params.toString());

        response = request(url + "?get=report", params);

        long endTime = System.currentTimeMillis();

        Logger.info("ServerConnectorService", "Loading report from server... COMPLETE in " + (endTime - startTime) + "ms");

        return response;
    }

    private Document request(String url, Map<String, String> params) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        HttpURLConnection conn = makeConnection(url, params);

        int responseCode = conn.getResponseCode();

        Logger.info("ServerConnectorService", "Result code: " + responseCode);

        if (RESPONSE_CODE_OK == responseCode) {
            response = readResponse(conn);
        }

        conn.disconnect();
        return response;
    }

    private Document readResponse(HttpURLConnection conn) throws IOException, SAXException, ParserConfigurationException {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        return builder.parse(conn.getInputStream());
    }

    private HttpURLConnection makeConnection(String url, Map<String, String> params) throws IOException {
        URL fullUrl = new URL(url);

        HttpURLConnection conn = (HttpURLConnection) fullUrl.openConnection();
        conn.setRequestMethod("POST");
        conn.setUseCaches(false);
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type",
                "application/x-www-form-urlencoded;charset=" + "UTF-8");
        OutputStream out = conn.getOutputStream();
        boolean first = true;
        for (String param : params.keySet()) {
            if (!first)
                out.write('&');
            else
                first = false;
            out.write(URLEncoder.encode(param, "UTF-8")
                    .getBytes("US-ASCII"));
            out.write('=');
            out.write(URLEncoder.encode(params.get(param), "UTF-8")
                    .getBytes("US-ASCII"));
        }
        out.flush();
        out.close();
        conn.connect();
        return conn;
    }


    public Document sendOrder(String game, int turn, String race, String password, String commands) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;
        long startTime = System.currentTimeMillis();

        Logger.info("ServerConnectorService", "Sending order...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);
        params.put("TURN", Integer.toString(turn));
        params.put("TYPE", "order");
        params.put("BODY", commands);

        Logger.debug("ServerConnectorService", params.toString());

        response = request(url + "?send=data", params);

        long endTime = System.currentTimeMillis();

        Logger.info("ServerConnectorService", "Sending order... COMPLETE in " + (endTime - startTime) + "ms");

        return response;
    }

    public Document races(String game) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Loading list of races...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);

        response = request(url + "?get=races", params);

        Logger.info("ServerConnectorService", "Loading list of races... COMPLETE");

        return response;
    }


    public Document loadContext(String game, String race, String password) throws IOException, SAXException, ParserConfigurationException {
        Document response = null;

        Logger.info("ServerConnectorService", "Loading context from server...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);

        response = request(url + "?get=context", params);

        Logger.info("ServerConnectorService", "Loading context from server... COMPLETE");

        return response;
    }

    public Document sendMessage(String type, String game, String race, String password, String to, String body) throws IOException, SAXException, ParserConfigurationException {

        Document response = null;
		String header = null;

        if ("mail".equalsIgnoreCase(type)) {
            header = "#MAIL " + game + ' ' + to + ' ' + game + ' ' + password;
        }

        if ("broadcast".equalsIgnoreCase(type)) {
            header = "#WALL " + game + ' ' + password;
        }

        if (header == null) {
            throw new IllegalArgumentException("Header can't be empty!");
        }

        Logger.info("ServerConnectorService", "Sending mail...");

        Map<String, String> params = new HashMap<String, String>();
        params.put("GAME", game);
        params.put("RACE", race);
        params.put("PASSWORD", password);
        params.put("TYPE", "mail");
        params.put("BODY", convertMailBody(header, body));

        response = request(url + "?send=data", params);

        Logger.info("ServerConnectorService", "Sending context from server... COMPLETE");

        return response;
    }


    private String convertMailBody(String header, String body) {
        StringBuilder builder = new StringBuilder();
        builder.append(header).append("\r\n");
        builder.append(body).append("\r\n");
        return builder.toString();
    }
}